package com.gsafety.wellsfort.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.gsafety.wellsfort.annotation.CheckToken;
import com.gsafety.wellsfort.common.constant.IConstant;
import com.gsafety.wellsfort.common.context.LoginContext;
import com.gsafety.wellsfort.common.context.LoginContextUtil;
import com.gsafety.wellsfort.common.enums.ClassEnum;
import com.gsafety.wellsfort.common.enums.RedisPrefixEnum;
import com.gsafety.wellsfort.common.enums.UserLoginEnum;
import com.gsafety.wellsfort.common.util.JwtUtil;
import com.gsafety.wellsfort.common.util.ResultCode;
import com.gsafety.wellsfort.config.CacheManager;
import com.gsafety.wellsfort.domain.bo.UserBO;
import com.gsafety.wellsfort.domain.model.organization.Student;
import com.gsafety.wellsfort.domain.model.security.Admin;
import com.gsafety.wellsfort.domain.model.security.UserLogin;
import com.gsafety.wellsfort.exception.TokenException;
import com.gsafety.wellsfort.service.organization.IStudentService;
import com.gsafety.wellsfort.service.security.IAdminService;
import com.gsafety.wellsfort.service.security.IUserLoginService;
import com.sun.org.apache.bcel.internal.generic.ICONST;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.asm.Advice;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;

/**
 * @author tangjiawei
 * @version 1.0
 * @date 2019/6/17 13:52
 */
@Slf4j
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    IUserLoginService userLoginService;

    @Autowired
    IAdminService adminService;

    @Autowired
    IStudentService studentService;

    @Autowired
    private CacheManager cacheManager;

    /**
     * 默认token
     */
    public static final String DEFAULT_TOKEN = "AaEeDdBbCcEe11FF22GG33";

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {

        // 从 http 请求头中取出 token
        String token = httpServletRequest.getHeader(IConstant.TOKEN);
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //如果方法设置 checkToken注解, value=false 则表示放行
        if (method.isAnnotationPresent(CheckToken.class)) {
            CheckToken checkToken = method.getAnnotation(CheckToken.class);
            if (!checkToken.value()) {
                return true;
            }
        }
        // 执行认证
        if (StringUtils.isEmpty(token)) {
            throw new TokenException(ResultCode.INVALID_TOKEN);
        }
        Map<String, String> accountMap = (Map) cacheManager.getHash(RedisPrefixEnum.ACCOUNT_TOKEN_UNIQUE.getPrefix());
        if (accountMap == null || !accountMap.containsValue(token)) {
            if (token.equals(DEFAULT_TOKEN)) {
                //游客模式
                UserLogin userLoginDo = userLoginService.getUserLoginById(UserLoginEnum.VISITOR.getValue());
                // 成功登录,重置token
                token = JwtUtil.createJWT(12 * 60 * 60 * 1000, userLoginDo);
            } else {
                log.info("redis没有缓存token数据,返回token失效!");
                throw new TokenException(ResultCode.USER_EXPIRE);
            }
        }
        // 获取 token 中的 knowledge id
        Integer userId;
        try {
            userId = JWT.decode(token).getClaim("id").asInt();
        } catch (JWTDecodeException j) {
            throw new TokenException(ResultCode.INVALID_TOKEN);
        }
        UserLogin userLogin = userLoginService.getUserLoginById(userId);
        if (userLogin == null) {
            throw new TokenException(ResultCode.NOT_FOUND_USER);
        }
        Boolean verify = JwtUtil.isVerify(token, userLogin);
        if (!verify) {
            throw new TokenException(ResultCode.USER_EXPIRE);
        }
        log.info("用户信息={}", userLogin);
        //存储进入ThreadLocal
        LoginContextUtil.setContext(buildContext(userLogin));
        return true;
    }

    /**
     * 构建全局对象
     *
     * @param userLogin
     * @return
     */
    private LoginContext buildContext(UserLogin userLogin) {
        LoginContext loginContext = new LoginContext();
        Admin admin = adminService.getByLoginId(userLogin.getId());
        Student student = studentService.getAllByLoginId(userLogin.getId());
        loginContext.setAdmin(admin);
        loginContext.setStudent(student);
        UserBO userBO = convertUserLogin2Bo(userLogin, admin);
        loginContext.setUserBO(userBO);
        return loginContext;
    }

    private UserBO convertUserLogin2Bo(UserLogin userLogin, Admin admin) {
        UserBO userBO = UserBO.builder().uid(userLogin.getId()).account(userLogin.getAccount()).
                type(userLogin.getType().intValue()).status(userLogin.getStatus().intValue()).build();
        if (admin != null && admin.getName() != null) {
            userBO.setName(admin.getName());
        }
        return userBO;
    }


    /**
     * 在用户登录时,获取设备信息,并且将设备信息 来源(android||ios存入org_user_login表)
     * 将设备id和ios存入org_user_login表 id 绑定,用户注销时退出绑定
     *
     * @param userId
     * @param httpServletRequest
     */
    public void bindUserInfo(Integer userId, HttpServletRequest httpServletRequest) {
        String deviceId = httpServletRequest.getHeader(IConstant.DEVICE_ID);
        String plantForm = httpServletRequest.getHeader(IConstant.PLANT_FORM);
        log.info("app端请求参数设备信息={},设备来源={}", deviceId, plantForm);
        if (!Optional.ofNullable(deviceId).isPresent() && !Optional.ofNullable(plantForm).isPresent()) {
            UserLogin userLogin = UserLogin.builder().ext2(plantForm).ext3(deviceId).id(userId).build();
            userLoginService.bindUserInfo(userLogin);
        }
    }


    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}
